package com.cndcare.msc.handler;

import cn.hutool.core.util.RandomUtil;
import com.cndcare.msc.constants.Constants;
import com.cndcare.msc.gui.GlobleHotKeyDemo;
import com.cndcare.msc.gui.RightCornerPopMessage;
import com.cndcare.msc.protobuf.MessageVo;
import com.cndcare.msc.protobuf.ObjDecoder;
import com.cndcare.msc.protobuf.ObjEncoder;
import com.cndcare.msc.util.SpringBeanFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * netty客户端
 */
@Component
public class NettyClient {
    public Logger log = LoggerFactory.getLogger(this.getClass());

    public static String HOST;
    public static String PORT;
    public static int READER_IDLE_TIME_SECONDS = 30; // 读操作空闲20秒
    public static int WRITER_IDLE_TIME_SECONDS = 30; // 写操作空闲20秒
    public static int ALL_IDLE_TIME_SECONDS = 60; // 读写全部空闲40秒

    private EventLoopGroup loop = new NioEventLoopGroup();

    public void run() {
        try {
            doConnect(new Bootstrap(), loop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * netty client 连接，连接失败5秒后重试连接
     */
    public Bootstrap doConnect(Bootstrap bootstrap, EventLoopGroup eventLoopGroup) {
        log.info("doConnect");
        System.setProperty("java.awt.headless", "false");
        log.info("热键监听开启");
        GlobleHotKeyDemo.getInstance();
        log.info("右上方提示开启");
        RightCornerPopMessage.getInstance();
        try {
            if (bootstrap != null) {
                bootstrap.group(eventLoopGroup);
                bootstrap.channel(NioSocketChannel.class);
                bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();

                        p.addLast("idleStateHandler", new IdleStateHandler(READER_IDLE_TIME_SECONDS
                                , WRITER_IDLE_TIME_SECONDS, ALL_IDLE_TIME_SECONDS, TimeUnit.SECONDS));

                        p.addLast(new ObjDecoder(MessageVo.class));
                        p.addLast(new ObjEncoder(MessageVo.class));

                        p.addLast("heartHandler", new HeartHandler(NettyClient.this));

                    }
                });
                Map<String, Object> socketMap = getSocket();
                String host = MapUtils.getString(socketMap, "host", "127.0.0.1");
                Integer tcpPort = MapUtils.getInteger(socketMap, "tcpPort", 9999);

                bootstrap.remoteAddress(host, tcpPort);
                ChannelFuture f = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
                    final EventLoop eventLoop = futureListener.channel().eventLoop();
                    if (!futureListener.isSuccess()) {
                        log.warn("连接服务器失败，5s后重新尝试连接！");
                        futureListener.channel().eventLoop().schedule(() -> doConnect(new Bootstrap(), eventLoop), 5, TimeUnit.SECONDS);
                    }
                });
                f.channel().closeFuture().sync();
                // 注释打开，断线不会重连，会被回收
//                eventLoopGroup.shutdownGracefully();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bootstrap;
    }

    private Map<String, Object> getSocket() {
        Map<String, Object> socketMap = new HashMap<>();
        try {
            StringRedisTemplate stringRedisTemplate = SpringBeanFactory.getBean("stringRedisTemplate", StringRedisTemplate.class);
            String str = stringRedisTemplate.opsForValue().get(Constants.NETTY_SERVER_KEY);
            ObjectMapper objectMapper = new ObjectMapper();
            List list = objectMapper.readValue(str, List.class);
            log.info("list = {}", list);
            Map<String, Object> map = (Map<String, Object>) list.get(RandomUtil.randomInt(list.size()));
            String host = MapUtils.getString(map, "host", "127.0.0.1");
            Integer tcpPort = MapUtils.getInteger(map, "tcpPort", 9999);
            socketMap.put("host", host);
            socketMap.put("tcpPort", tcpPort);
            log.info("host = {}", host);
            log.info("tcpPort = {}", tcpPort);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return socketMap;
        }
    }
}
